/**
 * Copyright (c) The openTCS Authors.
 *
 * This program is free software and subject to the MIT license. (For details,
 * see the licensing information (LICENSE.txt) you should have received with
 * this copy of the software.)
 */
package org.opentcs.virtualvehicle.inputcomponents;

import javax.swing.text.Document;

/**
 * General input panel with three text fields and optionally:
 * <ul>
 * <li>a message/text</li>
 * <li>labels for the text fields</li>
 * <li>unit labels for the text fields</li>
 * </ul>
 * The input of the text fields can be validated 
 * (see {@link Builder#enableValidation enableValidation}).
 * For instanciation the contained 
 * {@link TripleTextInputPanel.Builder Builder}-class must be used.
 * The <code>Object</code> that is returned by {@link InputPanel#getInput} is
 * an <code>Array</code> of three <code>Strings</code>.
 *
 * @author Tobias Marquardt (Fraunhofer IML)
 */
public final class TripleTextInputPanel
    extends TextInputPanel {

  /**
   * If the panel is resetable this is the value the input is set to when
   * doReset() is called.
   */
  private Object resetValue;
  /**
   * Flag indicating if the content of the first text field is a valid input.
   */
  private boolean inputField1Valid = true;
  /**
   * Flag indicating if the content of the second text field is a valid input.
   */
  private boolean inputField2Valid = true;
  /**
   * Flag indicating if the content of the third text field is a valid input.
   */
  private boolean inputField3Valid = true;

  /** 
   * Create a new <code>TripleTextInputPanel</code>.
   * @param title Title of the panel
   */
  private TripleTextInputPanel(String title) {
    super(title);
    initComponents();
  }

  /**
   * Enable input validation against the given regular expressions. 
   * If a format string is <code>null</code> the related text field is not 
   * validated.
   * @see InputPanel#addValidationListener
   * @param format1 A regular expression for the first text field.
   * @param format2 A regular expression for the second text field.
   * @param format3 A regular expression for the third text field.
   */
  private void enableInputValidation(String format1,
                                     String format2,
                                     String format3) {
    if (format1 != null) {
      inputField1.getDocument().addDocumentListener(new TextInputValidator(format1));
    }
    if (format2 != null) {
      inputField2.getDocument().addDocumentListener(new TextInputValidator(format2));
    }
    if (format3 != null) {
      inputField3.getDocument().addDocumentListener(new TextInputValidator(format3));
    }
  }

  @Override
  protected void captureInput() {
    input = new String[] {inputField1.getText(),
                          inputField2.getText(),
                          inputField3.getText()};
  }

  @Override
  public void doReset() {
    input = resetValue;
  }

  /**
   * Mark the input of the specified <code>Document</code> as valid, if this
   * Document belongs to one of the three text fields in this panel.
   * If <code>valid</code> is <code>true</code> the validity of the other
   * two documents is checked, too. Only if all three text fields contain valid
   * input, the whole input of the panel is marked as valid.
   *
   * @see TextInputPanel#setInputValid(boolean, javax.swing.text.Document)  
   * @param valid true, if the content of the <code>Document</code> is valid
   * @param doc the <code>Document</code>
   */
  @Override
  protected void setInputValid(boolean valid, Document doc) {
    // Find out to which input field the document belongs and check the others
    boolean allValid = valid;
    if (doc == inputField1.getDocument()) {
      inputField1Valid = valid;
      if (!(inputField2Valid && inputField3Valid)) {
        allValid = false;
      }
    }
    else if (doc == inputField2.getDocument()) {
      inputField2Valid = valid;
      if (!(inputField1Valid && inputField3Valid)) {
        allValid = false;
      }
    }
    else if (doc == inputField3.getDocument()) {
      inputField3Valid = valid;
      if (!(inputField1Valid && inputField2Valid)) {
        allValid = false;
      }
    }
    else {
      allValid = false;
    }
    setInputValid(allValid);
  }

  // CHECKSTYLE:OFF
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    unitLabel3 = new javax.swing.JLabel();
    label3 = new javax.swing.JLabel();
    inputField3 = new javax.swing.JTextField();
    messageLabel = new javax.swing.JLabel();
    label1 = new javax.swing.JLabel();
    inputField1 = new javax.swing.JTextField();
    unitLabel1 = new javax.swing.JLabel();
    label2 = new javax.swing.JLabel();
    inputField2 = new javax.swing.JTextField();
    unitLabel2 = new javax.swing.JLabel();

    setLayout(new java.awt.GridBagLayout());

    unitLabel3.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    unitLabel3.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
    unitLabel3.setText("Unit-Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(unitLabel3, gridBagConstraints);

    label3.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    label3.setText("Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(label3, gridBagConstraints);

    inputField3.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    inputField3.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    inputField3.setText("initial Value");
    inputField3.setName("inputField3"); // NOI18N
    inputField3.setPreferredSize(new java.awt.Dimension(70, 20));
    inputField3.addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(java.awt.event.FocusEvent evt) {
        inputField3FocusGained(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 3;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(inputField3, gridBagConstraints);
    inputField3.getAccessibleContext().setAccessibleName("");

    messageLabel.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    messageLabel.setText("Message");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.gridwidth = 3;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(messageLabel, gridBagConstraints);

    label1.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    label1.setText("Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(label1, gridBagConstraints);

    inputField1.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    inputField1.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    inputField1.setText("initial Value");
    inputField1.setName("inputField1"); // NOI18N
    inputField1.setPreferredSize(new java.awt.Dimension(70, 20));
    inputField1.addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(java.awt.event.FocusEvent evt) {
        inputField1FocusGained(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(inputField1, gridBagConstraints);
    inputField1.getAccessibleContext().setAccessibleName("");

    unitLabel1.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    unitLabel1.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
    unitLabel1.setText("Unit-Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(unitLabel1, gridBagConstraints);

    label2.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    label2.setText("Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(label2, gridBagConstraints);

    inputField2.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    inputField2.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    inputField2.setText("initial Value");
    inputField2.setName("inputField2"); // NOI18N
    inputField2.setPreferredSize(new java.awt.Dimension(70, 20));
    inputField2.addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(java.awt.event.FocusEvent evt) {
        inputField2FocusGained(evt);
      }
    });
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(inputField2, gridBagConstraints);
    inputField2.getAccessibleContext().setAccessibleName("");

    unitLabel2.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
    unitLabel2.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
    unitLabel2.setText("Unit-Label");
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 2;
    gridBagConstraints.gridy = 2;
    gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
    gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
    add(unitLabel2, gridBagConstraints);
  }// </editor-fold>//GEN-END:initComponents

  private void inputField1FocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_inputField1FocusGained
    inputField1.selectAll();
  }//GEN-LAST:event_inputField1FocusGained

  private void inputField2FocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_inputField2FocusGained
    inputField2.selectAll();
  }//GEN-LAST:event_inputField2FocusGained

  private void inputField3FocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_inputField3FocusGained
    inputField3.selectAll();
  }//GEN-LAST:event_inputField3FocusGained
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JTextField inputField1;
  private javax.swing.JTextField inputField2;
  private javax.swing.JTextField inputField3;
  private javax.swing.JLabel label1;
  private javax.swing.JLabel label2;
  private javax.swing.JLabel label3;
  private javax.swing.JLabel messageLabel;
  private javax.swing.JLabel unitLabel1;
  private javax.swing.JLabel unitLabel2;
  private javax.swing.JLabel unitLabel3;
  // End of variables declaration//GEN-END:variables
  //CHECKSTYLE:ON

  /**
   * See {@link InputPanel.Builder}.
   */
  public static class Builder
      implements InputPanel.Builder {

    /**
     * The panel's title.
     */
    private final String title;
    /**
     * Message to be displayed in the panel.
     */
    private String message;
    /**
     * Labels of for the text fields.
     */
    private final String[] labels = new String[3];
    /**
     * Unit labels of the text fields.
     */
    private final String[] unitLabels = new String[3];
    /**
     * Initial values for the text fields.
     */
    private final String[] initialValues = new String[3];
    /**
     * Regular expressions for validation of the text field contents.
     */
    private final String[] formats = new String[3];
    /**
     * Show a reset button in the panel.
     * Default is <code>false</code>. 
     */
    private boolean resetButton;
    /**
     * Value the input is reset to when the reset button is used.
     */
    private Object resetValue;

    /**
     * Create a new builder.
     * @param title Title of the panel.
     */
    public Builder(String title) {
      this.title = title;
    }

    @Override
    public InputPanel build() {
      TripleTextInputPanel panel = new TripleTextInputPanel(title);
      panel.enableInputValidation(formats[0], formats[1], formats[2]);
      panel.label1.setText(labels[0]);
      panel.label2.setText(labels[1]);
      panel.label3.setText(labels[2]);
      panel.unitLabel1.setText(unitLabels[0]);
      panel.unitLabel2.setText(unitLabels[1]);
      panel.unitLabel3.setText(unitLabels[2]);
      panel.inputField1.setText(initialValues[0]);
      panel.inputField2.setText(initialValues[1]);
      panel.inputField3.setText(initialValues[2]);
      panel.messageLabel.setText(message);
      panel.resetable = resetButton;
      if (panel.resetable) {
        panel.resetValue = resetValue;
      }
      return panel;
    }

    /**
     * Set the labels for the text fields. 
     * Passing <code>null</code> means there should not be such a label.
     * @param label1 The label of the first text field
     * @param label2 The label of the second text field
     * @param label3 The label of the third text field
     * @return the instance of this <code>Builder</code>
     */
    public Builder setLabels(String label1, String label2, String label3) {
      labels[0] = label1;
      labels[1] = label2;
      labels[2] = label3;
      return this;
    }

    /**
     * Set the same initial value for all three text fields.
     * @param value The initial value.
     * @return the isntance of this <code>Builder</code>
     */
    public Builder setInitialValues(String value) {
      return setInitialValues(value, value, value);
    }

    /**
     * Set the initial values for the text fields of the panel
     * Passing <code>null</code> means there should not be an initial value
     * for this text field.
     * @param val1 The initial value of the first text field
     * @param val2 The initial value of the second text field
     * @param val3 The initial value of the third text field
     * @return the instance of this <code>Builder</code>
     */
    public Builder setInitialValues(String val1, String val2, String val3) {
      initialValues[0] = val1;
      initialValues[1] = val2;
      initialValues[2] = val3;
      return this;
    }

    /**
     * Set the same text for the unit label for all three text fields.
     * @param unit The unit 
     * @return the isntance of this <code>Builder</code>
     */
    public Builder setUnitLabels(String unit) {
      return setUnitLabels(unit, unit, unit);
    }

    /**
     * Set the text for the unit labels of the panel.
     * Passing <code>null</code> means there should not be a unit label for
     * the corresponding text field.
     * @param unit1 The unit of the first text field
     * @param unit2 The unit of the second text field
     * @param unit3 The unit of the third text field
     * @return the instance of this <code>Builder</code>
     */
    public Builder setUnitLabels(String unit1, String unit2, String unit3) {
      unitLabels[0] = unit1;
      unitLabels[1] = unit2;
      unitLabels[2] = unit3;
      return this;
    }

    /**
     * Set the message of the panel.
     * The user of this method must take care for the line breaks in the message,
     * as it is not wrapped automatically!
     * @param message the message
     * @return the instance of this <code>Builder</code>
     */
    public Builder setMessage(String message) {
      this.message = message;
      return this;
    }

    public Builder enableValidation(String format) {
      return enableValidation(format, format, format);
    }

    /**
     * Make the panel validate it's input by using the specified regular
     * expressions for the text fields.
     * Passing null as an argument means that the corresponding text field
     * should not be validated.
     * @param format1 The regular expression that will be used for validation in
     *                the first text field.
     * @param format2 The regular expression that will be used for validation in
     *                the second text field.
     * @param format3 The regular expression that will be used for validation in
     *                the third text field.
     * @return the instance of this <code>Builder</code>
     */
    public Builder enableValidation(String format1,
                                    String format2,
                                    String format3) {
      formats[0] = format1;
      formats[1] = format2;
      formats[2] = format3;
      return this;
    }

    /**
     * Set a value the panel's input can be reset to.
     * @param resetValue the reset value
     * @return the instance of this <code>Builder</code>
     */
    public Builder enableResetButton(Object resetValue) {
      this.resetButton = true;
      this.resetValue = resetValue;
      return this;
    }
  }
}
